package com.vdong.common.oauth.service.impl;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.github.sd4324530.fastweixin.api.response.GetUserInfoResponse;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.vdong.common.oauth.common.enums.*;
import com.vdong.common.oauth.common.exception.AppBizException;
import com.vdong.common.oauth.common.exception.ProcessException;
import com.vdong.common.oauth.common.util.CommonResult;
import com.vdong.common.oauth.common.util.RedisSequenceFactory;
import com.vdong.common.oauth.entity.dto.*;
import com.vdong.common.oauth.entity.mo.*;
import com.vdong.common.oauth.entity.vo.UserLoginVO;
import com.vdong.common.oauth.repository.dao.*;
import com.vdong.common.oauth.service.LoginService;
import com.vdong.common.oauth.service.WechatAuthorizerInfoService;
import com.vdong.kpay.basics.dubbo.api.frontend.AppUserService;
import com.vdong.kpay.basics.dubbo.pojo.frontend.AppUserMain;
import com.vdong.member.account.facade.AccountPeddleUserFacadeService;
import lombok.extern.slf4j.Slf4j;
import org.lamb.framework.common.remote.FacadeResult;
import org.lamb.framework.common.util.CopyUtil;
import org.lamb.framework.common.util.DateUtil;
import org.lamb.framework.common.util.FacadeResultUtils;
import org.lamb.framework.common.util.StringUtil;
import org.lamb.framework.core.annotation.LambValid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

import static org.springframework.util.ReflectionUtils.findMethod;
import static sun.security.x509.AuthorityKeyIdentifierExtension.SERIAL_NUMBER;


@Slf4j
@Component
public class LoginServiceImpl implements LoginService {

    @Autowired
    private WechatAuthorizerInfoService wechatAuthorizerInfoService;

    @Autowired
    private WechatAuthorSerivce wechatAuthorSerivce;

    @Resource
    private WechatAuthorizerInfoMapper wechatAuthorizerInfoMapper;

//    @Reference(version = "1.0.0")
//   private AppVipTaskService vipTaskService;

    @Autowired
    private UserMainMapper userMainMapper;

    @Autowired
    private AppUserInfoMapper userInfoMapper;

    @Autowired
    private AppStoreInfoMapper storeInfoMapper;

    @Reference
    private AppUserService appUserService;

    @Reference
    private AccountPeddleUserFacadeService accountPeddleUserFacadeService;
    @Resource
    private RedisSequenceFactory redisSequenceFactory;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private LoginService loginService;

    public static final Long LAMB_TOKEN_TIME = new Long(1800000);
    private static final int REDIS_KEY_EXPIRE_HOURS = 24; // redis键值失效小时

    /**
     * 类反射方法缓存 key-类名 value-类属性<key: , value:方法属性>
     */
    private static Map<Class<?>, Map<String, Method>> methodCache = new ConcurrentHashMap<Class<?>, Map<String, Method>>();

    @Override
    public CommonResult<String> execute(LoginParamDTO param) throws Exception {
        log.info("登陆execute,入参为:[{}]", JSON.toJSONString(param));
        Class<?> clazz = loginService.getClass();
        Map<String, Method> methodMap = methodCache.get(clazz);
        if (methodMap != null && methodMap.containsKey(param.getLoginMethod())) {
            Object result = excuteLoginMethod(methodMap, param);
            return CommonResult.buildSucc(result.toString());
        }

        synchronized (clazz) {
            Method method = findMethod(clazz, param.getLoginMethod(), null);
            if (method == null) {
                return CommonResult.buildFail(String.format("类 [%s] 不存在方法 [%s]",
                        clazz.getCanonicalName(), param.getLoginMethod()));
            } else {
                method.setAccessible(true);
                if (methodMap == null) {
                    methodMap = new ConcurrentHashMap<String, Method>();
                    methodCache.put(clazz, methodMap);
                }
                methodMap.put(param.getLoginMethod(), method);
            }
        }

        if (!methodMap.containsKey(param.getLoginMethod())) {
            return CommonResult.buildFail(String.format("类 [%s] 不存在方法 [%s]", clazz.getCanonicalName(), param.getLoginMethod()));
        }

        Object result = excuteLoginMethod(methodMap, param);
        log.info("返回的登陆信息为:[{}]", result.toString());
        return CommonResult.buildSucc(result.toString());
    }

    private Object excuteLoginMethod(Map<String, Method> methodMap, LoginParamDTO param) {
        Method method = methodMap.get(param.getLoginMethod());
        Object invoke = null;
        try {
            invoke = method.invoke(loginService, param);
            return invoke;
        } catch (Exception e) {
            InvocationTargetException targetEx = (InvocationTargetException) e;
            Throwable t = targetEx.getTargetException();
            t.printStackTrace();
            return t.getMessage();
        }
    }


    @Override
    public UserLoginVO loginH5Execute(String code) {

        WechatAuthorizerInfo wechatAuthorizerInfo = wechatAuthorizerInfoService.loadByStoreId(-1L, AuthorizeTypeEnum.Open);
        if (wechatAuthorizerInfo == null || wechatAuthorizerInfo.getStatus() == null || wechatAuthorizerInfo.getStatus() == 0) {
            throw new AppBizException("该店铺的公众号未授权");
        }
        GetUserInfoResponse getUserInfoResponse = wechatAuthorSerivce.getUserInfo(wechatAuthorizerInfo.getAppid(), code);
        AppUserLoginInfo userInfo = new AppUserLoginInfo();
        userInfo.setNickName(getUserInfoResponse.getNickname());
        userInfo.setAvatarUrl(getUserInfoResponse.getHeadimgurl());
        userInfo.setCountry(getUserInfoResponse.getCountry());
        userInfo.setProvince(getUserInfoResponse.getProvince());
        userInfo.setCity(getUserInfoResponse.getCity());
        userInfo.setLanguage(getUserInfoResponse.getLanguage());
        userInfo.setGender(getUserInfoResponse.getSex());
        userInfo.setOpenId(getUserInfoResponse.getOpenid());
        userInfo.setUnionId(getUserInfoResponse.getUnionid());
        PeddleUserDTO peddleUserDTO = doLogin(userInfo);
        return initialize(peddleUserDTO);
    }

    @Override
    public PeddleUserDTO doLogin(AppUserLoginInfo userInfo) {
        final String openid = userInfo.getOpenId();
        final String unionId = userInfo.getUnionId();
        if (Strings.isNullOrEmpty(unionId)) {
            throw new AppBizException("获取unionId失败");
        }
        PeddleUserDTO user1 = new PeddleUserDTO();
        user1.setWxUnionid(unionId);
        if (accountPeddleUserFacadeService == null) {
            log.info("-------------------accountPeddleUserFacadeService is null--------------------");
        }

        String userStr = accountPeddleUserFacadeService.getPeddleUserDetailByWxUnionid(JSON.toJSONString(user1));
        FacadeResult<PeddleUserDTO> facadeResult = FacadeResultUtils.stringToFacadeBean(userStr, PeddleUserDTO.class);
        boolean isSuccess = facadeResult.isSuccess();
        PeddleUserDTO userResult = new PeddleUserDTO();
        if (isSuccess) {
            userResult = facadeResult.getData();
        }
        String userfacade;
        if (null == userResult || userResult.getId() == null) {
            PeddleUserDTO user = new PeddleUserDTO();
            user.setWxOpenid(openid);
            user.setWxUnionid(unionId);
            user.setNickName(userInfo.getNickName());
            user.setHeadUrl(userInfo.getAvatarUrl());
            user.setSex(userInfo.getGender().toString());
            userfacade = accountPeddleUserFacadeService.addPeddleUser(JSON.toJSONString(user));
            FacadeResult<PeddleUserDTO> userfacadeResult = FacadeResultUtils.stringToFacadeBean(userfacade, PeddleUserDTO.class);
            boolean facadeResultSuccess = facadeResult.isSuccess();
            if (facadeResultSuccess) {
                userResult = userfacadeResult.getData();
            }

        } else {
            if (!Objects.equal(userResult.getNickName(), userInfo.getNickName())
                    || !Objects.equal(userResult.getHeadUrl(), userInfo.getAvatarUrl())
                    || !Objects.equal(userResult.getSex(), userInfo.getGender())) {
                userResult.setId(userResult.getId());
                userResult.setHeadUrl(userInfo.getAvatarUrl());
                userResult.setNickName(userInfo.getNickName());
                userResult.setWxUnionid(unionId);
                userResult.setWxOpenid(openid);
                userfacade = accountPeddleUserFacadeService.editPeddleUser(JSON.toJSONString(userResult));
                FacadeResult<PeddleUserDTO> userfacadeResult = FacadeResultUtils.stringToFacadeBean(userfacade, PeddleUserDTO.class);
                boolean facadeResultSuccess = facadeResult.isSuccess();
                if (facadeResultSuccess) {
                    userResult = userfacadeResult.getData();
                }
            }
        }
        return userResult;
    }

    public UserLoginVO initialize(PeddleUserDTO peddleUserDTO) {
        Long userId = peddleUserDTO.getId();
        String unionId = peddleUserDTO.getWxUnionid();
        String token = userId + "_" + unionId + "_" + UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(token, JSON.toJSONString(peddleUserDTO), LAMB_TOKEN_TIME.longValue(), TimeUnit.MILLISECONDS);
        return UserLoginVO.builder().user(peddleUserDTO).token(token).build();
    }

    @LambValid
    public String disbPublicLogin(@Valid @NotNull LoginParamDTO param) {
        log.info("分销H5登陆disbPublicLogin,入参为:[{}]", JSON.toJSONString(param));
        if (StringUtil.hasBlank(param.getCode())) throw new ProcessException(ProcessExceptionEnum.EB00000002);
        WechatAuthorizerInfoResultDTO dto = getWechatAuthorizerInfo(param, PlatformTypeEnum.DISB.getValue());
        GetUserInfoResponse userInfo = wechatAuthorSerivce.getUserInfo(dto.getAppid(), param.getCode());
        AppUserLoginInfoDTO appUserLoginInfoDTO = CopyUtil.copyObject(userInfo, new AppUserLoginInfoDTO());
        PeddleUserDTO peddleUserDTO = loginDisbExcute(dto.getStoreId(), appUserLoginInfoDTO);
        String token = peddleUserDTO.getId() + "_" + appUserLoginInfoDTO.getUnionid() + "_" +
                UUID.randomUUID().toString().replace("-", "");
        redisTemplate.opsForValue().set(token, JSON.toJSONString(peddleUserDTO),
                LAMB_TOKEN_TIME.longValue(), TimeUnit.MILLISECONDS);
        return JSON.toJSONString(LoginResultDTO.builder().user(JSON.toJSONString(peddleUserDTO)).token(token).build());
    }

    private PeddleUserDTO loginDisbExcute(Long storeId, AppUserLoginInfoDTO userInfo) {
        if (StringUtil.hasBlank(userInfo.getOpenid(), userInfo.getUnionid())) {
            throw new ProcessException(ProcessExceptionEnum.EB00000007);
        }
        PeddleUserDTO userResult = new PeddleUserDTO();
        try {
            FacadeResult<PeddleUserDTO> result = FacadeResultUtils.stringToFacadeBean(accountPeddleUserFacadeService
                    .getPeddleUserDetailByWxUnionid(JSON.toJSONString(PeddleUserDTO.builder()
                            .wxUnionid(userInfo.getUnionid()).build())), PeddleUserDTO.class);
            if (result.isSuccess()) userResult = result.getData();
            if (StringUtil.hasBlank(userResult, userResult.getId())) {//user不存在
                PeddleUserDTO build = PeddleUserDTO.builder().wxOpenid(userInfo.getOpenid())
                        .wxUnionid(userInfo.getUnionid()).nickName(userInfo.getNickname())
                        .headUrl(userInfo.getHeadimgurl()).sex(userInfo.getSex().toString()).build();
                FacadeResult<PeddleUserDTO> facadeResult = FacadeResultUtils.stringToFacadeBean(
                        accountPeddleUserFacadeService.addPeddleUser(JSON.toJSONString(build)), PeddleUserDTO.class);
                if (facadeResult.isSuccess()) userResult = facadeResult.getData();
            } else {//user存在
                if (!Objects.equal(userResult.getNickName(), userInfo.getNickname()) || !Objects.equal(userResult.getHeadUrl()
                        , userInfo.getHeadimgurl()) || !Objects.equal(userResult.getSex(), userInfo.getSex())) {
                    userResult.setId(userResult.getId());
                    userResult.setHeadUrl(userInfo.getHeadimgurl());
                    userResult.setNickName(userInfo.getNickname());
                    userResult.setWxUnionid(userInfo.getUnionid());
                    userResult.setWxOpenid(userInfo.getOpenid());
                    FacadeResult<PeddleUserDTO> facadeResult = FacadeResultUtils
                            .stringToFacadeBean(accountPeddleUserFacadeService
                                    .editPeddleUser(JSON.toJSONString(userResult)), PeddleUserDTO.class);
                    if (facadeResult.isSuccess()) userResult = facadeResult.getData();
                }
            }
        } catch (Exception e) {
            log.info("loginDisbExcute,捕获到的异常信息未:[{}]", e);
            throw new ProcessException(ProcessExceptionEnum.EB00000009);
        }
        return userResult;
    }

    private WechatAuthorizerInfoResultDTO getWechatAuthorizerInfo(LoginParamDTO param, Integer code) {
        log.info("getWechatAuthorizerInfo,入参为:[{}]", JSON.toJSONString(param));
        List<WechatAuthorizerInfo> result = null;
        if (PlatformTypeEnum.STUDY.getValue().equals(code)) {
            result = wechatAuthorizerInfoMapper.selectByAppId(param.getAppId(), AuthorizeTypeEnum.Open.getCode());
        } else if (PlatformTypeEnum.DISB.getValue().equals(code)) {
            result = wechatAuthorizerInfoMapper.selectByStoreId(-1L, AuthorizeTypeEnum.Open.getCode());
        }
        if (result.size() < 1) throw new ProcessException(ProcessExceptionEnum.EB00000003);
        WechatAuthorizerInfo wechatAuthorizerInfo = result.get(0);
        WechatAuthorizerInfoResultDTO dto = CopyUtil.copyObject(result.get(0), new WechatAuthorizerInfoResultDTO());
        if (StringUtil.hasBlank(dto) || WechatAuthorTypeEnum.UNAUTHORIZED.getValue().equals(dto.getStatus())) {
            throw new ProcessException(ProcessExceptionEnum.EB00000004);
        }
        return dto;
    }

    @LambValid
    public String animalPublicLogin(@Valid @NotNull LoginParamDTO param) {
        log.info("学兽H5登陆animalPublicLogin,入参为:[{}]", JSON.toJSONString(param));
        if (StringUtil.hasBlank(param.getAppId(), param.getCode()))
            throw new ProcessException(ProcessExceptionEnum.EB00000002);
        AppUserMain appUserMain = appUserService.loginH5Execute(Long.parseLong(param.getStoreId()), param.getCode());
        String key = Constants.RedisKeyPrefix.USER + param.getStoreId() + "_" + appUserMain.getId();
        String token = param.getStoreId() + "_" + appUserMain.getId() + "_" + UUID.randomUUID()
                .toString().replace("-", "");
        redisTemplate.opsForValue().set(key, token, REDIS_KEY_EXPIRE_HOURS, TimeUnit.HOURS);
        redisTemplate.opsForValue().set(token, JSON.toJSONString(appUserMain), REDIS_KEY_EXPIRE_HOURS, TimeUnit.HOURS);
        return JSON.toJSONString(LoginResultDTO.builder().user(JSON.toJSONString(appUserMain)).token(token).build());
//        WechatAuthorizerInfoResultDTO dto = getWechatAuthorizerInfo(param, PlatformTypeEnum.STUDY.getValue());
//        GetUserInfoResponse userInfo = wechatAuthorSerivce.getUserInfo(dto.getAppid(), param.getCode());
//        AppUserLoginInfoDTO appUserLoginInfoDTO = CopyUtil.copyObject(userInfo, new AppUserLoginInfoDTO());
//        return loginStudyExcute(dto.getStoreId(), appUserLoginInfoDTO);
    }

    private String loginStudyExcute(Long storeId, AppUserLoginInfoDTO userInfo) {
        if (StringUtil.hasBlank(userInfo.getOpenid(), userInfo.getUnionid())) {
            throw new ProcessException(ProcessExceptionEnum.EB00000007);
        }
        List<AppUserMainMO> result = userMainMapper.select(AppUserMainMO.builder().storeId(storeId)
                .wxUnionid(userInfo.getUnionid()).build());
        AppUserMainMO user = null;
        if (result.size() < 1) {//user存在
            //判断店铺是否存在
            AppStoreInfoMO store = storeInfoMapper.selectByPrimaryKey(storeId);
            if (StringUtil.hasBlank(store)) throw new ProcessException(ProcessExceptionEnum.EB00000008);
            String userCode = generate(BizCodeEnum.USER, store.getStorePrefix());
            user = AppUserMainMO.builder().storeId(storeId).createDate(new Date()).wxOpenid(userInfo.getOpenid())
                    .wxUnionid(userInfo.getUnionid()).nickName(userInfo.getNickname()).headUrl(userInfo.getHeadimgurl())
                    .roleId(new Byte("3")).code(userCode).build();
            int insert = userMainMapper.insert(user);
            if (insert < 1) throw new ProcessException(ProcessExceptionEnum.EB00000011);

            AppUserInfoMO build = AppUserInfoMO.builder().id(user.getId()).storeId(user.getStoreId())
                    .createDate(new Date()).applyStatus(new Byte("0")).sex(userInfo.getSex().byteValue()).build();
            int userInfoRow = userInfoMapper.insert(build);
            if (userInfoRow < 1) throw new ProcessException(ProcessExceptionEnum.EB00000011);
        } else {//user不存在
            user = result.get(0);
            if (!Objects.equal(user.getNickName(), userInfo.getNickname())
                    || !Objects.equal(user.getHeadUrl(), userInfo.getHeadimgurl())
                    || !Objects.equal(user.getWxOpenid(), userInfo.getOpenid())) {
                user.setId(user.getId());
                user.setHeadUrl(userInfo.getHeadimgurl());
                user.setNickName(userInfo.getNickname());
                user.setWxUnionid(userInfo.getUnionid());
                user.setWxOpenid(user.getWxOpenid());
                int row = userMainMapper.updateByPrimaryKeySelective(user);
                if (row < 1) throw new ProcessException(ProcessExceptionEnum.EB00000011);
            }
        }
//        vipTaskService.vipLogin(storeId, user.getId());
        String key = Constants.RedisKeyPrefix.USER + storeId + "_" + user.getId();
        String token = storeId + "_" + user.getId() + "_" + UUID.randomUUID()
                .toString().replace("-", "");
        redisTemplate.opsForValue().set(key, token, REDIS_KEY_EXPIRE_HOURS, TimeUnit.HOURS);
        redisTemplate.opsForValue().set(token, JSON.toJSONString(user), REDIS_KEY_EXPIRE_HOURS, TimeUnit.HOURS);
        return JSON.toJSONString(LoginResultDTO.builder().user(JSON.toJSONString(user)).token(token).build());
    }

    private String generate(BizCodeEnum bizCode, String prefix) {
        if (StringUtil.hasBlank(bizCode)) throw new ProcessException(ProcessExceptionEnum.EB00000010);
        Date today = DateUtil.getDayBegin(new Date());
        String dateStr = DateUtil.getDateString(today);
        Date dayEnd = DateUtil.getDayEnd(new Date());
        switch (bizCode) {
            case ORDER:
            case USER:
            case COURSE:
            case GROUPBUY:
            case QUESTION:
            case STORE:
                //构造redis的key
                String key = SERIAL_NUMBER + dateStr + ":" + bizCode.getType();
                Long incr = redisSequenceFactory.generate(key, dayEnd);
                //默认编码需要3位，位数不够前面补0
                String formattNum = String.format("%03d", incr);
                //订单编号：DD+年月日+店铺编号后3位+3位顺序号
                return bizCode.getType() +
                        dateStr +
                        prefix +
                        formattNum;
            case BANNER:
            case COURSE_CAT://KCFL+一级分类后三位+3位顺序号；
                //构造redis的key
                String key2 = SERIAL_NUMBER + dateStr + ":" + bizCode.getType();
                Long incr2 = redisSequenceFactory.generate(key2, dayEnd);
                //默认编码需要3位，位数不够前面补0
                String formattNum2 = String.format("%03d", incr2);
                return bizCode.getType() +
                        prefix +
                        formattNum2;
            default:
                return "";
        }
    }

    public String animalRoutineLogin(LoginParamDTO param) {
        log.info("学兽小程序登陆animalRoutineLogin,入参为:[{}]", JSON.toJSONString(param));
        return null;
    }

    @LambValid
    public static String saasLogin(@Valid @NotNull Object param) {
        log.info("SAAS登陆saasLogin,入参为:[{}]", JSON.toJSONString(param));
//        if (StringUtil.hasBlank(param.getUsername(), param.getPassword()))
//            throw new ProcessException(ProcessExceptionEnum.EB00000002);
//        if (StringUtil.hasBlank(param.getUsername(), param.getPassword()))
//            throw new ProcessException(ProcessExceptionEnum.EB00000002);
//        AuthWebAuthenticationDetails details = new AuthWebAuthenticationDetails(param.getRequest());

        return "qqqqq";
    }

    @LambValid
    public String merchantLogin(@Valid @NotNull LoginParamDTO param) {
        log.info("商户登陆merchantLogin,入参为:[{}]", JSON.toJSONString(param));
        return null;
    }
}
